/*
 *     *********************************************************************
 *     * Copyright (C) 1988, 1990 Stanford University.                     *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  Stanford University                 *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

/*
 * Incremental simulation
 */

#include <stdio.h>
#include "defs.h"
#include "net.h"
#include "globals.h"

void walk_net( int  ( * func )( nptr n, char * arg ), char * arg );

/*
 * Set H to the next "effective" change in the history following Hp
 */
#define	NEXTH( H, Hp )	for( (H) = (Hp)->next; (H)->punt; (H) = (H)->next )


/*
 * time at which history entry was enqueued
 */
#define	QTIME( H )		( (H)->time - (H)->t.r.delay )
#define	QPTIME( H )		( (H)->time - (H)->t.p.delay )
#define	PuntTime( H )		( (H)->time - (H)->t.p.ptime )


#define	IsDeviated( ND )	( (ND)->nflags & (DEVIATED | CHANGED) or (ND)->events != NULL )
#define	NeedUpdate( ND )	( ( (ND)->nflags & (ACTIVE_CL | STIM | POWER_RAIL) ) == 0 )


public	long	INC_RES = 0;		/* time resolution in ticks (.5 ns) */
public	nptr	inc_cause = ( nptr ) 1;	/* fake cause ptr for path command */

public	long    nevals = 0;		/* # of evaluations */
public	long    i_nevals = 0;		/* # of incremental evaluations */
private	long    o_nevals, o_nevent;

public	long	nreval_ev = 0;		/* number of various events */
public	long	npunted_ev = 0;
public	long	nstimuli_ev = 0;
public	long	ncheckpt_ev = 0;
public	long	ndelaychk_ev = 0;
public	long	ndelay_ev = 0;

private	void	( *curr_model )( Node * );	/* the current model used to eval */
private	hptr	modelp;			/* model history */

#ifdef FAULT_SIM
#define	TRIGGER_EV	0x81

public	int	fault_mode = FALSE;	/* TRUE when doing faultsim */
private	int	stop_early;		/* TRUE when fault detected */
private	long	old_cur_delta;		/* cur_delta before faultsim */
private	evptr	pending_evs;		/* pending events before faultsim */
extern	int	do_trigger();
extern	void	setup_triggers();

#define	DoingFault	( fault_mode != FALSE )
#define	NotDoingFault	( fault_mode == FALSE )
#else
#define	DoingFault	(0)
#define	NotDoingFault	(1)
#define	hchange		head
#endif


typedef struct {		/* transistor stage built by GetConnList */
   int   flags;			/* see below */
   nptr  nd_list;			/* the connection list */
   nptr  inp_list;			/* input nodes connected to list */
} Stage, *pstg;

/* stage flags */
#define	ALL_MERGED	0x1		/* all nodes in stage are converged */
#define	ONLY_INPUT	0x2		/* list consists of input node only */
#define	INP_TRANS	0x4		/* there are switching inputs */


/*
 * Update the state of a node from the history.  Set the INPUT flag as well.
 * Return a pointer to the next change in the history.
 */
private	hptr UpdateNode( nptr nd ) {
   register hptr cur, nxt;

   cur = nd->curr;
   if( cur->time > cur_delta )
      cur = ( hptr ) &( nd->head );

   NEXTH( nxt, cur );
   while( nxt->time <= cur_delta ) {
      cur = nxt;
      NEXTH( nxt, nxt );
   }

   nd->curr = cur;
   nd->npot = cur->val;
   if( cur->inp )
      nd->nflags |= INPUT;
   else
      nd->nflags &= ~INPUT;

   return( nxt );
}


/*
 * Activate a node: Set the ACTIVE_CL flag, schedule all pending transitions
 * as of cur_delta.  Punted events that were already punted (as of cur_delta)
 * remain in the history.  Pending punted events are rescheduled.  Punted
 * events between nd->curr and the next transition that were queued after
 * cur_delta are removed from the history and put in nd->t.punts.
 */
private void ActivateNode( nptr  nd ) {
   register hptr  h, p;
   hptr           *lastp;
   int            first;

   #ifdef INCDEBUG
   if( nd->nflags & WATCHED ) {
      lprintf( stdout, "%.2f: Activate %s\n", d2ns( cur_delta ), pnode( nd ) );
   }
   #endif
   if( nd->nflags & STIM ) {
      /* don't dequeue if ntime == now */
      if( nd->c.event->ntime != cur_delta )
         DequeueEvent( nd );
      nd->nflags &= ~STIM;
      NEXTH( p, nd->curr );
   } else
      p = UpdateNode( nd );

   ( void ) EnqueueHist( nd, p, CHECK_PNT );
   nd->nflags |= ( ACTIVE_CL | WAS_ACTIVE );

   lastp = &( nd->t.punts );
   *lastp = NULL;
   /* look for any pending events as of cur_delta */
   first = TRUE;
   for( p = nd->curr, h = p->next; ; p = h, h = h->next ) {
      if( h->punt ) {
         if( PuntTime( h ) < cur_delta )	/* already punted, skip it */
            continue;

         if( QPTIME( h ) <= cur_delta ) {	/* pending: enqueue it */
            if( QPTIME( h ) != cur_delta )	/* cur_delta: do not queue */
               ( void ) EnqueueHist( nd, h, PUNTED );
            if( DoingFault )
               continue;
            p->next = h->next;		/* and remove it from hist */
            h->next = freeHist;
            freeHist = h;
            h = p;
         } else if( first and NotDoingFault ) {
            /* move if punted before next transition */
            p->next = h->next;
            h->next = NULL;
            *lastp = h;
            lastp = &( h->next );
            h = p;
         }
      } else {
         first = FALSE;
         if( QTIME( h ) < cur_delta )	/* pending: enqueue it */
            ( void ) EnqueueHist( nd, h, REVAL );
         else
            break;
      }
   }
}


/*
 * Define all possible cases due to an event on an active node, as follows:
 *	000 0: no edge, node remains converged (not deviated)
 *	001 1: no edge, node just converged
 *	010 2: no edge, node just deviated
 *	011 3: no edge, node remains deviated
 *	100 4: edge, node remains converged
 *	101 5: edge, node just converged
 *	110 6: edge, node just deviated
 *	111 7: edge, node remains deviated
 */

#define	O_DEV		0x1		/* node was previously deviated */
#define	N_DEV		0x2		/* node is now deviated */
#define	EDGE		0x4		/* a transistion ocurred */
#define	CHK		0x8		/* just dequeued a check_pnt event */
#define	SAME_T		0x10		/* event merges at the same time */
#define	N_ACTV		0x20		/* merge events: still active xtors */

#define	GetOdev( nd )		( (nd)->nflags & DEVIATED )

private	evptr    dev_evs;		/* deviate events */
private	evptr    mrg_evs;		/* merge events w/transition */
private	evptr    *last_mrg;		/* last merge event w/transition */
private	evptr    mrg0_evs;		/* last merge event */
private	evptr    chk_evs;		/* events with no transition */
private	evptr    inp_evs;		/* input events */
private	evptr    xinp_evs;		/* stop being an input events */
private	evptr    stim_evs;		/* stimulus events */
private	evptr    pend_evs;		/* pending events */


/*
 * Change the event type to reflect new state, add event to its corresponding
 * event list, set the oldpot field and the DEVIATED bit for the node.
 */
#define	Deviate_Edge( ND, EV, Oval, Chk )		\
  {							\
    (EV)->type = GetOdev( ND ) | N_DEV | Chk | EDGE;	\
    (EV)->nlink = dev_evs;				\
    dev_evs = (EV);					\
    (ND)->oldpot = Oval;				\
    (ND)->nflags |= DEVIATED;				\
  }							\
 

#define	Deviate( ND, EV, Oval, Chk )			\
  {							\
    (EV)->type = GetOdev( ND ) | N_DEV | Chk;		\
    (EV)->nlink = chk_evs;				\
    chk_evs = (EV);					\
    (ND)->oldpot = Oval;				\
    (ND)->nflags |= DEVIATED;				\
  }							\
 

#define	Deviate_SameVal( EV )				\
  {							\
    (EV)->type = N_DEV | O_DEV | EDGE;			\
    (EV)->nlink = dev_evs;				\
    dev_evs = (EV);					\
  }							\
 

/*
 * Change the event type to reflect new state, add event to its corresponding
 * event list and reset the DEVIATED bit.
 */
#define Merge_Edge( ND, EV, Chk )			\
  {							\
    if( (ND)->nflags & DEVIATED )			\
      {							\
	(EV)->type = (O_DEV | EDGE | Chk );		\
	*last_mrg = (EV);				\
	last_mrg = & (EV)->nlink;			\
      }							\
    else						\
      {							\
	(EV)->type = (Chk | EDGE);			\
	(EV)->nlink = mrg0_evs;				\
	mrg0_evs = (EV);				\
      }							\
    (ND)->nflags &= ~DEVIATED;				\
  }							\
 

#define Merge_SameEdge( ND, EV, Chk )			\
  {							\
    (EV)->type = GetOdev( ND ) | SAME_T | Chk | EDGE;	\
    (EV)->nlink = mrg0_evs;				\
    mrg0_evs = (EV);					\
    (ND)->nflags &= ~DEVIATED;				\
  }							\
 

#define Merge( ND, EV, Chk )				\
  {							\
    (EV)->type = GetOdev( ND ) | Chk;			\
    (EV)->nlink = chk_evs;				\
    chk_evs = (EV);					\
    (ND)->nflags &= ~DEVIATED;				\
  }							\
 

/*
 * Free all punted events currently in nd->t.punts and move all punted events
 * after nd->curr to nd->t.punts.
 */
private void ReplacePunts( nptr nd ) {
   register hptr  h, p;

   if( DoingFault )
      return;
   h = nd->t.punts;
   if( h != NULL ) {
      for( p = h; p->next != NULL; p = p->next );
      p->next = freeHist;
      freeHist = h;
   }

   h = nd->curr;
   for( p = h; p->next->punt; p = p->next );
   if( p->punt ) {
      nd->t.punts = h->next;
      h->next = p->next;
      p->next = NULL;
   } else
      nd->t.punts = NULL;
}


/*
 * Update the state of a node due to a re-evaluation event.
 */
private void UpdateReval( evptr e ) {
   register nptr   n;			/* node in question */
   register evptr  nxte;		/* next chk_point event (if any) */
   register long   d_t;		/* delta time between events */

   n = e->enode;
   nxte = n->c.event;

   /* no chk_point event or chk_point is too far in the future */
   if( nxte == NULL or ( d_t = nxte->ntime - e->ntime ) > INC_RES ) {
      free_from_node( e, n );
      NewEdge( n, e );
      if( n->nflags & DEVIATED ) {
         if( n->oldpot == e->eval )
            Merge_Edge( n, e, 0 )
            else
               Deviate_SameVal( e )
            } else
         Deviate_Edge( n, e, n->npot, 0 );
      n->npot = e->eval;
   } else if( d_t != 0 ) {
      free_from_node( e, n );

      switch( nxte->type ) {
      case DELAY_CHK :
         ndelaychk_ev++;
         if( nxte->eval == e->eval ) {
            n->curr = nxte->p.hist;	/* update curr pointer */
            Merge_Edge( n, e, CHK );
            ReplacePunts( n );
         } else {
            DeleteNextEdge( n );
            NewEdge( n, e );
            Deviate_Edge( n, e, nxte->eval, CHK );
         }
         DequeueEvent( n );
         n->npot = e->eval;
         break;

      case DELAY_EV :
         if( nxte->eval == e->eval )
            DelayEvent( e, d_t );		/* delay again */
         else {
            lprintf( stdout,
                     "Missed Glitch: %s => (%.2f %c) (%.2f %c)\n",
                     pnode( n ), d2ns( nxte->p.oldt ), "0XX1"[nxte->eval],
                     d2ns( e->ntime ), "0XX1"[e->eval] );
            nxte->type = CHECK_PNT;
            NewEdge( n, e );
            Deviate_Edge( n, e, n->npot, 0 );
            n->npot = e->eval;
         }
         break;

      case CHECK_PNT :
         if( n->nflags & DEVIATED ) {
            if( n->oldpot == e->eval )
               Merge_Edge( n, e, 0 )
               else
                  Deviate_SameVal( e )

                  NewEdge( n, e );
            n->npot = e->eval;
         } else if( nxte->eval == e->eval ) {
            register evptr  ne;

            if( ( ne = n->events ) != NULL )
               while( ne->nlink != NULL )
                  ne = ne->nlink;

            if( ne == NULL or ne->ntime > nxte->ntime ) {
               nxte->p.oldt = e->ntime;
               nxte->type = DELAY_EV;
               DelayEvent( e, d_t );
            } else {
               NewEdge( n, e );
               Deviate_Edge( n, e, n->npot, 0 );
               n->npot = e->eval;
            }
         } else {
            NewEdge( n, e );
            Deviate_Edge( n, e, n->npot, 0 );
            n->npot = e->eval;
         }
         break;

      case INP_EV :
      case XINP_EV :
         if( n->nflags & DEVIATED ) {
            if( n->oldpot == e->eval )
               Merge_Edge( n, e, 0 )
               else
                  Deviate_SameVal( e );
         } else
            Deviate_Edge( n, e, n->npot, 0 );

         n->npot = e->eval;
         NewEdge( n, e );
         break;

      default :
         lprintf( stderr, "Unexpected Event 0x(%x)\n", nxte->type );
      }
   }
}


#define CopyEvent( DST, SRC )		\
  {					\
    (DST)->p.hist = (SRC)->p.hist;	\
    (DST)->ntime = (SRC)->ntime;	\
    (DST)->rtime = (SRC)->rtime;	\
    (DST)->delay = (SRC)->delay;	\
    (DST)->eval = (SRC)->eval;		\
  }					\
 

private void UpdateCheck_Pnt( evptr e ) {
   register nptr   n;
   register evptr  nxte;
   register long   d_t;

   n = e->enode;
   if( ( nxte = n->events ) != NULL ) {
      while( nxte->nlink != NULL )
         nxte = nxte->nlink;
      d_t = nxte->ntime - e->ntime;
   }

   if( nxte == NULL or d_t > INC_RES ) {
      DeleteNextEdge( n );
      if( n->npot == e->eval )
         Merge( n, e, CHK )
         else
            Deviate( n, e, e->eval, CHK );
   } else if( d_t == 0 ) {
      free_from_node( nxte, n );
      if( e->eval == nxte->eval ) {
         n->curr = e->p.hist;
         if( DoingFault )  n->hchange = *n->curr, n->curr = &n->hchange;
         n->npot = e->eval;
         n->curr->t.r.delay = nxte->delay;
         if( e->rtime == nxte->rtime )
            Merge_SameEdge( n, e, CHK )
            else {
               n->curr->t.r.rtime = nxte->rtime;
               Merge_Edge( n, e, CHK );
            }
         ReplacePunts( n );
      } else {
         DeleteNextEdge( n );
         d_t = e->eval;		/* save old value temporarily */
         CopyEvent( e, nxte );	/* this is the new value! */
         NewEdge( n, e );
         n->npot = e->eval;
         Deviate_Edge( n, e, d_t, CHK );
      }
   } else {	/* 0 < d_t <= INC_RES */
      if( n->nflags & DEVIATED ) {
         DeleteNextEdge( n );
         if( n->npot == e->eval )
            Merge( n, e, CHK )
            else
               Deviate( n, e, e->eval, CHK );
      } else {
         register hptr   h;

         NEXTH( h, e->p.hist );
         if( nxte->eval == e->eval and h->time > nxte->ntime ) {
            e->type = DELAY_CHK;
            DelayEvent( e, d_t );
         } else {
            DeleteNextEdge( n );
            Deviate( n, e, e->eval, CHK );
         }
      }
   }
}


private void UpdateDelay_Chk( evptr e ) {
   register nptr   n;
   register evptr  nxte;
   register long   d_t;

   n = e->enode;
   if( ( nxte = n->events ) != NULL ) {
      while( nxte->nlink != NULL )
         nxte = nxte->nlink;
      d_t = nxte->ntime - e->ntime;
   }

   if( nxte != NULL and d_t == 0 and nxte->eval == e->eval ) {
      free_from_node( nxte, n );
      n->curr = e->p.hist;
      n->curr->t.r.delay = nxte->delay;
      if( e->rtime == nxte->rtime )
         Merge_SameEdge( n, e, CHK )
         else {
            n->curr->t.r.rtime = nxte->rtime;
            Merge_Edge( n, e, CHK );
         }
      n->npot = e->eval;
      ReplacePunts( n );
   } else {	/* event got punted (and maybe something else was queued) */
      DeleteNextEdge( n );
      if( nxte != NULL and d_t == 0 ) {
         free_from_node( nxte, n );
         d_t = e->eval;
         CopyEvent( e, nxte );
         Deviate_Edge( n, e, d_t, CHK );
         NewEdge( n, e );
         n->npot = e->eval;
      } else					/* nothing else was queued */
         Deviate( n, e, e->eval, CHK );
   }
}


private void UpdateDelay_Ev( evptr e ) {
   register nptr   n;
   register evptr  nxte;
   register long   d_t;

   n = e->enode;
   if( ( nxte = n->events ) != NULL ) {
      while( nxte->nlink != NULL )
         nxte = nxte->nlink;
      d_t = nxte->ntime - e->ntime;
   }

   if( nxte != NULL and d_t == 0 and nxte->eval == e->eval ) {
      register hptr   h;

      free_from_node( nxte, n );
      NEXTH( h, n->curr );
      n->curr = h;
      Merge_SameEdge( n, e, CHK );
      n->npot = e->eval;
      ReplacePunts( n );
   } else {	/* expected event was punted */
      lprintf( stdout, "Missed Glitch: %s => (%.2f %c) -> (~%.2f %c)\n",
               pnode( n ), d2ns( e->p.oldt ), "0XX1"[e->eval],
               d2ns( ( e->ntime + e->p.oldt )/2 ), "0XX1"[n->npot] );

      DeleteNextEdge( n );
      if( nxte != NULL and d_t == 0 ) {
         free_from_node( nxte, n );
         NewEdge( n, nxte );
         d_t = e->eval;
         CopyEvent( e, nxte );
         Deviate_Edge( n, e, d_t, CHK );
         n->npot = e->eval;
      } else
         Deviate( n, e, e->eval, CHK );
   }
}


private void update_nodes( evptr e ) {
   nptr   n;
   int    hgmi;

   /* clear event lists */

   dev_evs = chk_evs = mrg_evs = mrg0_evs = inp_evs = xinp_evs =
                                    stim_evs = pend_evs = NULL;
   last_mrg = &mrg_evs;

   do {
#ifdef STATS
      { extern int ev_hgm; if( ev_hgm ) IncHistEvCnt( ( Uint ) e->type ); }
#endif STATS
      switch( e->type ) {
      case PUNTED :
         npunted_ev++;
         UpdateReval( e );
         break;

      case REVAL :
      case DECAY_EV :
         nreval_ev++;
         UpdateReval( e );
         break;

      case DELAY_CHK :
         ndelaychk_ev++;
         UpdateDelay_Chk( e );
         break;

      case DELAY_EV :
         ndelay_ev++;
         UpdateDelay_Ev( e );
         break;

      case CHECK_PNT :
         ncheckpt_ev++;
         UpdateCheck_Pnt( e );
         break;

      case XINP_EV :
         ncheckpt_ev++;
         n = e->enode;
         n->curr = e->p.hist;
         n->nflags &= ~( INPUT | ACTIVE_CL );
         e->nlink = xinp_evs;
         xinp_evs = e;
         ReplacePunts( n );
         break;

      case INP_EV :
         ncheckpt_ev++;
         n = e->enode;
         while( n->events and n->events->ntime != cur_delta )
            free_event( n->events );
         ReplacePunts( n );
         n->curr = e->p.hist;
         n->npot = e->eval;
         n->nflags |= INPUT;
         n->nflags &= ~DEVIATED;
         e->nlink = inp_evs;
         inp_evs = e;
         break;

      case STIM_INP :
         nstimuli_ev++;
         n = e->enode;
         n->npot = e->eval;
         n->curr = e->p.hist;
         n->nflags |= INPUT;
         e->nlink = stim_evs;
         stim_evs = e;
         break;

      case STIM_XINP : {
         register hptr  h;

         nstimuli_ev++;
         n = e->enode;
         n->curr = e->p.hist;
         n->nflags &= ~INPUT;
         NEXTH( h, n->curr );
         if( not EnqueueHist( n, h, STIMULI ) )
            n->nflags &= ~STIM;
         break;
      }

      case STIMULI :
         nstimuli_ev++;
         n = e->enode;
         n->npot = e->eval;
         n->curr = e->p.hist;
         e->nlink = stim_evs;
         stim_evs = e;
         break;

      case PENDING :
         e->nlink = pend_evs;
         pend_evs = e;
         break;
#ifdef notdef
      case PUNTED :
         npunted_ev++;
         n = e->enode;
         /* else: punt along with chk_pnt, the later handles it */
         if( n->c.event == NULL or n->c.event->ntime != e->ntime ) {
            int  oval;

            free_from_node( e, n );
            NewEdge( n, e );
            oval = ( n->nflags & DEVIATED ) ? n->oldpot : n->npot;
            Deviate_Edge( n, e, oval, 0 );
            n->npot = e->eval;
         }
         break;
#endif
      case CHNG_MODEL :
         curr_model = model_table[ modelp->val ];
         modelp = modelp->next;
         if( modelp != NULL )
            ( void ) EnqueueOther( CHNG_MODEL, ( long ) modelp->time );
         break;

#ifdef FAULT_SIM
      case TRIGGER_EV :
         stop_early = do_trigger( e );
         break;
#endif
      default:
         lprintf( stderr,
                  "update_nodes: bad event (%x) @ delta=%d for node %s\n",
                  e->type, cur_delta, pnode( e->enode ) );
      }
      npending -= 1;
   } while( ( e = e->flink ) != NULL );

   *last_mrg = mrg0_evs;	/* join the 2 lists, order is important */
}


/*
 * Run through the various event lists and update the state of all transistors
 * controlled by the node that triggered the event.  Also mark all nodes that
 * need to be considered for evaluation as VISITED, this doesn't imply they
 * will be evaluated.
 */
private void UpdateTransistors() {
   register evptr  e;
   register lptr   l;
   register tptr   t;

   for( e = dev_evs; e != NULL; e = e->nlink ) {
      if( ( e->type & O_DEV ) == 0 ) {	/* just deviated */
         for( l = e->enode->ngate; l != NULL; l = l->next ) {
            t = l->xtor;
            if( t->tflags & ACTIVE_T )
               t->state = compute_trans_state( t );
            t->source->nflags |= VISITED;	/* always mark src/drn */
            t->drain->nflags |= VISITED;
         }
      } else {				/* was already deviated */
         for( l = e->enode->ngate; l != NULL; l = l->next ) {
            t = l->xtor;
            if( t->tflags & ACTIVE_T ) {
               t->state = compute_trans_state( t );
               t->source->nflags |= VISITED;
               t->drain->nflags |= VISITED;
            }
         }
      }
   }

   for( e = mrg_evs; e != NULL; e = e->nlink ) {
      for( l = e->enode->ngate; l != NULL; l = l->next ) {
         t = l->xtor;
         if( t->tflags & ACTIVE_T ) {
            t->state = compute_trans_state( t );
            t->source->nflags |= VISITED;
            t->drain->nflags |= VISITED;
         }
      }
   }

   for( e = chk_evs; e != NULL; e = e->nlink ) {
      if( ( e->type & ( N_DEV | O_DEV ) ) == N_DEV ) {	/* just deviated */
         for( l = e->enode->ngate; l != NULL; l = l->next ) {
            t = l->xtor;
            if( t->tflags & ACTIVE_T )
               t->state = compute_trans_state( t );
            t->source->nflags |= VISITED;	/* always mark src/drn */
            t->drain->nflags |= VISITED;
         }
      }
   }

   for( e = inp_evs; e != NULL; e = e->nlink ) {
      for( l = e->enode->ngate; l != NULL; l = l->next ) {
         t = l->xtor;
         if( t->tflags & ACTIVE_T ) {
            t->state = compute_trans_state( t );
            t->source->nflags |= VISITED;
            t->drain->nflags |= VISITED;
         }
      }
   }

   for( e = xinp_evs; e != NULL; e = e->nlink )
      e->enode->nflags |= VISITED;

   for( e = stim_evs; e != NULL; e = e->nlink ) {
      for( l = e->enode->ngate; l != NULL; l = l->next ) {
         t = l->xtor;
         if( t->tflags & ACTIVE_T ) {
            t->state = compute_trans_state( t );
            t->source->nflags |= VISITED;
            t->drain->nflags |= VISITED;
         }
      }
   }

   VDD_node->nflags &= ~VISITED;
   GND_node->nflags &= ~VISITED;
}


#define hash_terms( T )         ( (Ulong)((T)->source) ^ (Ulong)((T)->drain) )


/*
 * Build the connection list for node 'n'.  Determine (1) if all nodes in the
 * list are converged or whether this list is adjacent to a transistor whose
 * gate is deviated, (2) if there are any inactive nodes (or transistors) in
 * the connection list, and (3) whether any inputs (transistor gates) to the
 * connection list have a transition at the current time.  As a side effect
 * set the 'withdriven' flag.  The algorithm to construct the connection list
 * is the same as that used by BuildConnList.
 *
 * We must be careful not to report as an input transition nodes that stop
 * being inputs (i.e. hist->delay == 0 and hist->inp == 0).
 */
#define IsCurrTransition( H )			\
  ( (H)->time == cur_delta and ((H)->inp == 1 or (H)->t.r.delay != 0) )

private pstg GetConnList( nptr n ) {
   register nptr  next, thisList, other, *inext;
   register lptr  l;
   register tptr  t;
   register hptr  h;
   register int   status;
   int            n_par = 0;
   static Stage   stage;

   stage.nd_list = n;
   stage.inp_list = NULL;

   status = ALL_MERGED;
   withdriven = FALSE;

   n->nflags &= ~VISITED;
   inext = &( stage.inp_list );

   if( NeedUpdate( n ) )
      ( void ) UpdateNode( n );
   if( IsDeviated( n ) )
      status &= ~ALL_MERGED;

   if( n->nflags & INPUT ) {
      stage.flags = ALL_MERGED | ONLY_INPUT;
      return( &stage );
   }

   next = thisList = n->nlink = n;
   do {
      for( l = thisList->nterm; l != NULL; l = l->next ) {
         t = l->xtor;
         if( not ( t->tflags & ACTIVE_T ) ) {
            if( t->ttype & GATELIST ) {
               for( t = ( tptr ) t->gate; t != NULL; t = t->scache.t )
                  if( NeedUpdate( t->gate ) )
                     ( void ) UpdateNode( t->gate );
               t = l->xtor;
            } else if( NeedUpdate( t->gate ) )
               ( void ) UpdateNode( t->gate );

            t->state = compute_trans_state( t );
         } else if( status & ALL_MERGED ) {
            if( t->ttype & GATELIST ) {
               for( t = ( tptr ) t->gate; t != NULL; t = t->scache.t )
                  if( t->gate->nflags & DEVIATED ) {
                     status &= ~ALL_MERGED;
                     break;
                  }
               t = l->xtor;
            } else if( t->gate->nflags & DEVIATED )
               status &= ~ALL_MERGED;
         }

         if( t->state == OFF )
            continue;

         if( t->tflags & CROSSED ) {
            t->tflags &= ~CROSSED;
            continue;
         }

         t->scache.r = t->dcache.r = NULL;

         other = other_node( t, thisList );

         if( NeedUpdate( other ) )
            ( void ) UpdateNode( other );
         if( IsDeviated( other ) )
            status &= ~ALL_MERGED;

         if( other->nflags & INPUT ) {
            withdriven = TRUE;
            if( other->nlink == NULL and
                  not ( other->nflags & ( ACTIVE_CL | POWER_RAIL ) ) ) {
               *inext = other;
               inext = &( other->nlink );
               *inext = other;		/* anything other than NULL */
            }
         } else {
            t->tflags |= CROSSED;
            if( other->nlink == NULL ) {
               other->nflags &= ~VISITED;
               other->nlink = n;
               next->nlink = other;
               next = other;
               other->n.tran = t;
            } else if( model_num != LIN_MODEL )
               goto is_trans;
            else if( hash_terms( other->n.tran ) == hash_terms( t ) ) {
               register tptr  tran = other->n.tran;

               if( tran->tflags & PARALLEL )
                  t->dcache.t = par_list( tran );
               else {
                  if( n_par >= MAX_PARALLEL ) {
                     WarnTooManyParallel( "a", "b" );
                     t->tflags |= PBROKEN;
                     continue;
                  }
                  tran->n_par = n_par++;
                  tran->tflags |= PARALLEL;
               }
               par_list( tran ) = t;
               t->tflags |= PBROKEN;
            } else {
               t->tflags |= BROKEN;
               continue;
            }
         }
is_trans :
         if( t->ttype & GATELIST ) {
            for( t = ( tptr ) t->gate; t; t = t->scache.t ) {
               h = t->gate->curr;
               if( IsCurrTransition( h ) ) {
                  t = l->xtor;
                  status |= INP_TRANS;
                  break;
               }
            }
            t = l->xtor;
         } else {
            h = t->gate->curr;
            if( IsCurrTransition( h ) )
               status |= INP_TRANS;
         }
      }		/* end of for each nterm */
   } while( ( thisList = thisList->nlink ) != n );

   next->nlink = NULL;
   *inext = NULL;
   stage.flags = status;

   return( &stage );
}


/*
 * Activate the gate of a transistor to act as stimuli.  First check
 * that the node in question is not part of an active cluster or is
 * already acting as stimuli.
 */
#define	StimulateGate( GATE )				\
  {							\
    if( NeedUpdate( GATE ) )				\
      {							\
	register hptr nextH = UpdateNode( GATE );	\
	if( EnqueueHist( GATE, nextH, STIMULI ) )	\
	    (GATE)->nflags |= STIM;			\
      }							\
  }


/*
 * Activate all nodes and transistors in the connection list of node 'nd',
 * scheduling pending events on the outputs and activating any inputs as
 * stimuli.  This routine assumes that the connection list has already been
 * built GetConnList.  Also activate all INPUT nodes connected to the stage.
 * The routine returns TRUE if any of the nodes in the connection list has
 * any pending punted events, else FALSE;
 */
private void ActivateStage( pstg stg ) {
   register nptr  n;

   n = stg->nd_list;
   do {
      register lptr  l;
      register tptr  t;

      if( not ( n->nflags & ACTIVE_CL ) )
         ActivateNode( n );

      for( l = n->nterm; l != NULL; l = l->next ) {
         t = l->xtor;
         if( t->tflags & ACTIVE_T )
            continue;
         t->tflags |= ACTIVE_T;
         if( t->ttype & GATELIST ) {
            for( t = ( tptr ) t->gate; t != NULL; t = t->scache.t )
               StimulateGate( t->gate );
         } else
            StimulateGate( t->gate );
      }
   } while( ( n = n->nlink ) != NULL );

   n = stg->inp_list;		/* traverse inputs connected to stage */
   while( n != NULL ) {
      register nptr  next;

      if( not ( n->nflags & ( ACTIVE_CL | POWER_RAIL ) ) )
         ( void ) ActivateNode( n );

      next = n->nlink;
      n->nlink = NULL;
      n = next;
   }
}


/*
 * Merge any 'future' punted events from previous runs with the current
 * history for the node.  Note that events created prior to cur_delta are
 * simply discarded (if they belong in the history the should have been
 * recreated and re-punted).
 */
private void MergePunts( nptr nd ) {
   register hptr  h, p, nx;

   /* skip past any punted events already in the history */
   for( nx = nd->curr; nx->next->punt; nx = nx->next );

   p = nd->t.punts;
   do {
      h = p;
      p = p->next;
      if( QPTIME( h ) < cur_delta ) {	/* in the past, free it */
         h->next = freeHist;
         freeHist = h;
      } else {				/* in the future, merge */
         h->next = nx->next;
         nx->next = h;
         nx = h;
      }
   } while( p != NULL );

   nd->t.punts = NULL;
}


/*
 * Deactivate all nodes on the connection list and destroy the list.  If a
 * node has any gate connections then turn it into a stimulus node.  Also
 * deactivate any non-OFF transistor on the connection list and move any
 * punted events from the previous simulation that have yet to be enqueued
 * back into the history.  We can't deactivate OFF transistors because some
 * of them may be boundary transistors.
 * For all transistors in the stage, clear the src/drn caches and any flags
 * that may have been set when building stage.
 */
private void DeactivateStage( pstg stg, nptr skipnd ) {
   register nptr  n, next;

   for( n = stg->nd_list; n != NULL; n = next ) {
      register lptr  l;
      register tptr  t;

      next = n->nlink;
      n->nlink = NULL;

#ifdef INCDEBUG
      if( n->nflags & WATCHED )
         lprintf( stdout, "%.2f: Deactivate %s\n",d2ns( cur_delta ),pnode( n ) );
#endif
      if( n->nflags & ACTIVE_CL ) {
         if( n->c.event != NULL and n->c.event->ntime > cur_delta )
            DequeueEvent( n );
         n->nflags &= ~ACTIVE_CL;
         if( n->t.punts != NULL )
            MergePunts( n );
      }
      if( n->ngate != NULL and not ( n->nflags & STIM ) and n != skipnd ) {
         hptr  h;
         NEXTH( h, n->curr );
         if( EnqueueHist( n, h, STIMULI ) )
            n->nflags |= STIM;
      }

      for( l = n->nterm; l != NULL; l = l->next ) {
         t = l->xtor;
         t->dcache.r = NULL;		/* may be set for parallel xtors */

         if( ( t->tflags & ACTIVE_T ) and ( t->state != OFF or
                                            ( other_node( t, n )->nflags & INPUT ) )  )
            t->tflags = 0;
         else
            t->tflags &= ~( BROKEN | PBROKEN | PARALLEL );
      }
   }

   for( n = stg->inp_list; n != NULL; n = next ) {
      next = n->nlink;
      n->nlink = NULL;
   }
}



private void UndoStage( pstg stg ) {
   register nptr  n, next;
   register lptr  l;
   register tptr  t;

   for( n = stg->nd_list; n != NULL; n = next ) {
      next = n->nlink;
      n->nlink = NULL;

      for( l = n->nterm; l != NULL; l = l->next ) {
         t = l->xtor;
         t->dcache.r = NULL;
         t->tflags &= ~( BROKEN | PBROKEN | PARALLEL );
      }
   }

   for( n = stg->inp_list; n != NULL; n = next ) {
      next = n->nlink;
      n->nlink = NULL;
   }
}


/*
 * Evaluate the source/drain of all active transistors controlled by 'nd'.
 * Activate/Deactivate clusters as required.  Return TRUE if there are any
 * active transistors controlled by this node, FALSE otherwise.
 * If force is TRUE then evaluate even if all nodes in the clusters are
 * merged.
 */
private int EvalSrcDrn( nptr nd, int force ) {
   register lptr  l;
   register int   found;
   register nptr  nterm;
   pstg           stg;

   found = FALSE;
   cur_node = nd;
   for( l = nd->ngate; l != NULL; l = l->next ) {
      tptr  t = l->xtor;

      if( not( t->tflags & ACTIVE_T ) )
         continue;

      found = TRUE;
      nterm = t->source;
      if( nterm->nflags & VISITED ) {
         stg = GetConnList( nterm );
         if( stg->flags & ONLY_INPUT ) {
            if( not ( nterm->nflags & ( ACTIVE_CL | POWER_RAIL ) ) and
                  nd->nflags & DEVIATED )
               ( void ) ActivateNode( nterm );
         } else if( ( stg->flags & ALL_MERGED ) and not force )
            DeactivateStage( stg, ( nptr ) NULL );
         else {
            ActivateStage( stg );
            ( *curr_model )( nterm );
         }
      }

      nterm = t->drain;
      if( nterm->nflags & VISITED ) {
         stg = GetConnList( nterm );
         if( stg->flags & ONLY_INPUT ) {
            if( not ( nterm->nflags & ( ACTIVE_CL | POWER_RAIL ) ) and
                  nd->nflags & DEVIATED )
               ActivateNode( nterm );
         } else if( ( stg->flags & ALL_MERGED ) and not force )
            DeactivateStage( stg, ( nptr ) NULL );
         else {
            ActivateStage( stg );
            ( *curr_model )( nterm );
         }
      }
   }
   return( found );
}


/*
 * Evaluate the source/drain of ALL transistors controlled by 'nd', which just
 * deviated.  Different things should be done if a transition caused the node
 * to deviate or a lack of transition.
 */
private void EvalJustDeviated( nptr nd, int has_trans ) {
   pstg  stg;
   lptr  l;

   cur_node = nd;
   for( l = nd->ngate; l != NULL; l = l->next ) {
      register nptr  nterm;
      register tptr  t;

      t = l->xtor;
      nterm = t->source;
      if( nterm->nflags & VISITED ) {
         stg = GetConnList( nterm );
         if( stg->flags & ONLY_INPUT ) {
            if( not ( nterm->nflags & ( ACTIVE_CL | POWER_RAIL ) ) )
               ActivateNode( nterm );
         } else {
            ActivateStage( stg );
            if( has_trans or ( stg->flags & INP_TRANS ) )
               ( *curr_model )( nterm );
            else
               UndoStage( stg );
         }
      }

      nterm = t->drain;
      if( nterm->nflags & VISITED ) {
         stg = GetConnList( nterm );
         if( stg->flags & ONLY_INPUT ) {
            if( not ( nterm->nflags & ( ACTIVE_CL | POWER_RAIL ) ) )
               ActivateNode( nterm );
         } else {
            ActivateStage( stg );
            if( has_trans or ( stg->flags & INP_TRANS ) )
               ( *curr_model )( nterm );
            else
               UndoStage( stg );
         }
      }
      /* may happen if src/drn are inputs now */
      if( not ( t->tflags & ACTIVE_T ) ) {
         t->tflags |= ACTIVE_T;
         if( t->ttype & GATELIST ) {
            for( t = ( tptr ) t->gate; t != NULL; t = t->scache.t )
               StimulateGate( t->gate );
            t = l->xtor;
         }
         t->state = compute_trans_state( t );
      }
   }
}


private void EvalInputs() {
   register evptr  ev;
   register nptr   n;

   for( ev = inp_evs; ev != NULL; ev = ev->nlink ) {
      register nptr   other;
      register lptr   l;
      register tptr   t;
      int             act_tran = FALSE;

      n = cur_node = ev->enode;
      n->nflags &= ~ACTIVE_CL;	/* assume it won't be active */

      for( l = n->nterm; l != NULL; l = l->next ) {
         pstg  stg;

         t = l->xtor;
         other = other_node( t, n );
         stg = GetConnList( other );
         if( stg->flags & ALL_MERGED ) {
            if( not ( stg->flags & ONLY_INPUT ) )
               DeactivateStage( stg, ( nptr ) NULL );
            else if( t->tflags & ACTIVE_T )
               act_tran = TRUE;
         } else {
            ActivateStage( stg );
            ( *curr_model )( other );
         }
      }
      if( act_tran == TRUE and not ( n->nflags & ( POWER_RAIL | ACTIVE_CL ) ) ) {
         register hptr  h;

         NEXTH( h, n->curr );
         if( EnqueueHist( n, h, CHECK_PNT ) )
            n->nflags |= ACTIVE_CL;
      }
   }

   for( ev = inp_evs; ev != NULL; ev = ev->nlink ) {
      int   n_active;

      n = cur_node = ev->enode;
      n_active = EvalSrcDrn( n, FALSE );

      /*
       * if node is not active anymore and it controls any
       * active transistors turn it into a stimulus node
       */
      if( n_active == TRUE and not ( n->nflags & ACTIVE_CL ) ) {
         register hptr  h;

         NEXTH( h, n->curr );
         if( EnqueueHist( n, h, STIMULI ) )
            n->nflags |= STIM;
      }
   }
}


private void EvalStimulus() {
   evptr  ev;
   int    n_active;
   nptr   nd;

   for( ev = stim_evs; ev != NULL; ev = ev->nlink ) {
      nd = ev->enode;
      n_active = EvalSrcDrn( nd, FALSE );

      if( not n_active )			/* no active transistors */
         nd->nflags &= ~STIM;
      else if( nd->nflags & STIM ) {	/* enqueue next transition */
         register hptr  h;

         NEXTH( h, nd->curr );
         if( not EnqueueHist( nd, h, STIMULI ) )
            nd->nflags &= ~STIM;
      }
   }
}


private void EvalXinputs() {
   register evptr  ev;
   register nptr   n;
   pstg            stg;

   for( ev = xinp_evs; ev != NULL; ev = ev->nlink ) {
      n = cur_node = ev->enode;
      if( n->nflags & VISITED ) {
         stg = GetConnList( n );
         if( stg->flags & ALL_MERGED )
            DeactivateStage( stg, ( nptr ) NULL );
         else {
            ActivateStage( stg );
            ( *curr_model )( n );
         }
      }
   }
}


private void EvalEventList() {
   register evptr  e;
   register nptr   n;
   int             status;
   pstg            stg;

   for( e = dev_evs; e != NULL; e = e->nlink ) {
      n = e->enode;
      if( ( e->type & O_DEV ) == 0 )
         EvalJustDeviated( n, TRUE );
      else
         ( void ) EvalSrcDrn( n, FALSE );

      /* if the node is still active schedule its next CHECK_PNT event */
      if( ( n->nflags & ACTIVE_CL ) and ( e->type & CHK ) ) {
         hptr  h;

         NEXTH( h, n->curr );
         ( void ) EnqueueHist( n, h, CHECK_PNT );
      }
   }

   for( e = mrg_evs; e != NULL; e = e->nlink ) {
      status = ( ( e->type & ( SAME_T | O_DEV ) ) == O_DEV ) ? TRUE : FALSE;

      if( EvalSrcDrn( e->enode, status ) )
         e->type |= N_ACTV;
   }

   for( e = mrg_evs; e != NULL; e = e->nlink ) {
      n = e->enode;
      stg = GetConnList( n );
      if( stg->flags & ALL_MERGED )
         DeactivateStage( stg, ( e->type & N_ACTV ) ? ( nptr ) NULL : n );
      else
         UndoStage( stg );

      if( ( n->nflags & ACTIVE_CL ) and ( e->type & CHK ) ) {
         hptr  h;

         NEXTH( h, n->curr );
         ( void ) EnqueueHist( n, h, CHECK_PNT );
      }
   }

   for( e = chk_evs; e != NULL; e = e->nlink ) {
      n = e->enode;
      switch( e->type & ( EDGE| O_DEV | N_DEV ) ) {
      case 0 :
         lprintf( stderr, "warning: case 0 time=%.2f for %s\n",
                  d2ns( cur_delta ), pnode( n ) );
         break;

      case O_DEV :	/* Node just merged but no transition */
         stg = GetConnList( n );
         if( stg->flags & ALL_MERGED )
            DeactivateStage( stg, ( nptr ) NULL );
         else
            UndoStage( stg );
         break;

      case N_DEV :	/* Node just deviated but no transition */
         n = e->enode;
         EvalJustDeviated( n, FALSE );
         break;

      case O_DEV | N_DEV :/* Node stays deviated but no transition */
         break;		/* -- do nothing -- */

      default:
         lprintf( stderr, "bad chk event (0x%2x) @ t=%.2f\n", e->type,
                  d2ns( cur_delta ) );
      }

      if( ( n->nflags & ACTIVE_CL ) and ( e->type & CHK ) ) {
         hptr  h;

         NEXTH( h, n->curr );
         ( void ) EnqueueHist( n, h, CHECK_PNT );
      }
   }
}



/*
 * Events pending from a previous simulation run.  If the node is active at
 * this point, simply drop the event (the node is being resimulated) otherwise
 * re-schedule the pending event (as if it had been simulated).
*/
private void EvalPending() {
   register evptr  ev;
   register nptr   n;

   for( ev = pend_evs; ev != NULL; ev = ev->nlink ) {
      n = ev->enode;
      if( not ( n->nflags & ACTIVE_CL ) )
         enqueue_event( n, ( int ) ev->eval, ( long ) ev->delay, ( long ) ev->rtime );
   }
}


private void incstep( long  stop_time ) {
   evptr  evlist;
   long   step_t, refresh;

   refresh = ( stop_time < 10 ) ? 1 : stop_time / 10;
   step_t = cur_delta + refresh;
   if( DoingFault ) step_t = max_time;		/* never refresh */

   while( ( evlist = get_next_event( stop_time ) ) != NULL ) {
      update_nodes( evlist );
      UpdateTransistors();

      EvalEventList();

      if( inp_evs )	    EvalInputs();
      if( xinp_evs )	    EvalXinputs();
      if( stim_evs )	    EvalStimulus();
      if( pend_evs )	    EvalPending();

      FreeEventList( evlist );

      if( cur_delta >= step_t ) {
         do {
            lprintf( stdout, "time = %d.0\n", ( int ) d2ns( step_t ) );
            ( void ) fflush( stdout );
            step_t += refresh;
         } while( cur_delta >= step_t );
         if( analyzerON )
            UpdateWindow( cur_delta - 1 );
      }
      #ifdef FAULT_SIM
      if( stop_early or int_received ) {
         return;
      }
      #endif
   }
   cur_delta = stop_time;
   #ifndef FAULT_SIM
   if( analyzerON ) {
      UpdateWindow( cur_delta );
   }
   #endif
}


private int fix_inc_nodes( nptr  nd, char * unused ) {
   register hptr  h;
   register lptr  l;

   if( nd->nflags & ( ALIAS | MERGED ) )
      return( 0 );

   if( ( nd->nflags & ACTIVE_CL ) and nd->t.punts != NULL ) { /* shouldn't be */
      for( h = nd->t.punts; h->next != NULL; h = h->next );
      h->next = freeHist;
      freeHist = nd->t.punts;
   }

   if( nd->nflags & ( WAS_ACTIVE | CHANGED ) )
      nd->t.cause = inc_cause;

   nd->nflags &= ~( VISITED|CHANGED|DEVIATED|STIM|ACTIVE_CL|WAS_ACTIVE );

   NEXTH( h, nd->curr );
   if( h != last_hist ) {	/* inconsistent ? */
      register hptr  p;

      do {
         p = h;
         NEXTH( h, h );
      } while( h != last_hist );
      nd->curr = h = p;
   } else
      h = nd->curr;

   nd->c.time = h->time;
   nd->npot = h->val;
   if( h->inp )
      nd->nflags |= INPUT;
   else
      nd->nflags &= ~INPUT;

   for( l = nd->ngate; l != NULL; l = l->next ) {	/* fix transistors */
      register tptr  t = l->xtor;
      t->state = compute_trans_state( t );
      t->tflags &= ~ACTIVE_T;
   }
   for( l = on_trans; l != NULL; l = l->next )
      l->xtor->tflags &= ~ACTIVE_T;

   return( 0 );
}


/*
 * Start evaluating/activating the changed nodes.  This will schedulle other
 * events to get things rolling.  Special care is needed for changed nodes
 * that became inputs at time 0; we must ensure that their adjacent (src/drn)
 * transistors have the proper state, so things work out when the node
 * becomes undriven (if at all) and revaluated properly,
 */
private void startup_isim( nptr n ) {
   
   // n: the changed node
   
   pstg  stg;

   stg = GetConnList( n );
   ActivateStage( stg );
   if( stg->flags & INP_TRANS )	/* transition at time 0 => evaluate */
      ( *curr_model )( n );
   else if( stg->flags & ONLY_INPUT ) {	/* node is an input at time 0 */
      register lptr  l;
      register tptr  t;

      for( l = n->nterm; l != NULL; l = l->next ) {
         t = l->xtor;
         t->state = compute_trans_state( t );
      }
      UndoStage( stg );
   } else				/* no transitions at time 0, undo */
      UndoStage( stg );
}


/*
 * Main incremental simulation routine.  Move back to time 0, changing all
 * pending events to PENDING type.  Activate the clusters of all the
 * nodes in 'changed nodes' list and start simulating until we reach the
 * current time.  Make sure that the whole network is in a consistent state
 * before returning.
 */
public void incsim( iptr ch_list ) {
   long  stop_time;

   o_nevals = nevals;
   nevals = i_nevals;
   o_nevent = nevent;
   sm_stat |= INCR_SIM;

   stop_time = cur_delta;
   cur_delta = sim_time0;

   ( void ) back_sim_time( cur_delta, TRUE );

   modelp = first_model;			/* initialize the model */
   curr_model = model_table[ modelp->val ];
   modelp = modelp->next;
   if( modelp != NULL )
      ( void ) EnqueueOther( CHNG_MODEL, ( long ) modelp->time );

   if( ch_list != NULL ) {
      register iptr  ip;

      if( stop_time != 0 ) {	/* work to do ? */
         for( ip = ch_list; ip != NULL; ip = ip->next )
            ip->inode->nflags |= VISITED;	/* set VISITED on all nodes */
      } else {
         while( ( ip = ch_list ) != NULL ) {
            ch_list = ch_list->next;
            ip->inode->nflags &= ~( VISITED );
            FreeInput( ip );
         }
      }

      while( ( ip = ch_list ) != NULL ) {
         ch_list = ch_list->next;
         if( ip->inode->nflags & VISITED )
            startup_isim( ip->inode );
         FreeInput( ip );
      }
   }

   incstep( stop_time );

   rm_inc_events( FALSE );

   walk_net( fix_inc_nodes, ( char * ) 0 );

   sm_stat &= ~INCR_SIM;
   i_nevals = nevals;
   nevals = o_nevals;
   nevent = o_nevent;
}


#ifdef FAULT_SIM
public void init_faultsim() {
   o_nevals = nevals;
   nevals = i_nevals;
   o_nevent = nevent;
   sm_stat |= INCR_SIM;
   old_cur_delta = cur_delta;
   cur_delta = sim_time0;
   pending_evs = back_sim_time( cur_delta, TRUE + TRUE );
   fault_mode = TRUE;
}


public void end_faultsim() {
   walk_net( fix_inc_nodes, ( char * ) 0 );
   cur_delta = old_cur_delta;
   requeue_events( pending_evs, TRUE );
   fault_mode = FALSE;
   sm_stat &= ~INCR_SIM;
   i_nevals = nevals;
   nevals = o_nevals;
   nevent = o_nevent;
}


private int fix_fault_nodes( nptr nd ) {
   register lptr  l;

   if( not ( nd->nflags & ( ALIAS | MERGED ) ) ) {
      nd->nflags &= ~( VISITED|CHANGED|DEVIATED|STIM|ACTIVE_CL|WAS_ACTIVE );

      if( nd->curr == &nd->hchange ) {
         register hptr  h;

         NEXTH( h, nd->curr );
         nd->curr = ( h == last_hist ) ? &nd->head : h;
      }

      /* only mark transistors as non-activate for next faultsim */
      for( l = nd->ngate; l != NULL; l = l->next ) {
         register tptr  t = l->xtor;
         t->tflags &= ~ACTIVE_T;
      }
      for( l = on_trans; l != NULL; l = l->next )
         l->xtor->tflags &= ~ACTIVE_T;
   }
   return( 0 );
}


public void faultsim( nptr n ) {
   long   stop_time;

   stop_time = old_cur_delta;

   cur_delta = sim_time0;
   stop_early = 0;

   modelp = first_model;			/* initialize the model */
   curr_model = model_table[ modelp->val ];
   modelp = modelp->next;
   if( modelp != NULL )
      ( void ) EnqueueOther( CHNG_MODEL, ( long ) modelp->time );

   setup_triggers();

   n->nflags |= ( VISITED | CHANGED );
   startup_isim( n );

   incstep( stop_time );

   rm_inc_events( TRUE );

   walk_net( fix_fault_nodes, ( char * ) 0 );
}
#endif
